//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner.ActivityImportWizards
{
    using System;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.Properties;

    /// <summary>
    /// 
    /// </summary>
    class ActivityCodeGenerator
    {
        private ActivityCodeGenerator() { }

        private static string activitySkeleton;
        private static string depPropSkeleton;
        private static string executeSkeleton;
        private static string activityClassName;

        private const string OUTPUT_TYPE = "%%OUTPUT_TYPE%%";
        private const string OUTPUT_PARAMETER_NAME = "%%OUTPUT_PARAMETER_NAME%%";
        private const string VOID_OUTPUT_TYPE = "%%VOID_OUTPUT_TYPE%%";
        private const string PROXY_CLASS_NAME = "%%PROXY_CLASS_NAME%%";
        private const string PROXY_METHOD_NAME = "%%PROXY_METHOD_NAME%%";
        private const string PARAMETER_VALUES = "%%PARAMETER_VALUES%%";
        private const string METHOD_NAME = "%%METHOD_NAME%%";
        private const string METHOD_DESCRIPTION = "%%METHOD_DESCRIPTION%%";
        private const string CATEGORY_NAME = "%%CATEGORY_NAME%%";
        private const string EXECUTION_BODY = "%%EXECUTION_BODY%%";
        private const string DEPENDENCY_PROPERTIES = "%%DEPENDENCY_PROPERTIES%%";
        private const string ACTIVITY_NAMESPACE = "%%ACTIVITY_NAMESPACE%%";
        private const string PARAMETER_NAME = "%%PARAMETER_NAME%%";
        private const string PARAMETER_VALID_NAME = "%%PARAMETER_VALID_NAME%%";
        private const string PARAMETER_TYPE = "%%PARAMETER_TYPE%%";
        private const string ACTIVITY_CLASS_NAME = "%%ACTIVITY_CLASS_NAME%%";
        private const string INPUT_OUTPUT_ATTRIBUTE = "%%INPUT_OUTPUT_ATTRIBUTE%%";
        private const string REQUIRED_INPUT_PARAM_ATTRIBUTE = "[RequiredInputParam]";
        private const string OUTPUT_PARAM_ATTRIBUTE = "[OutputParam]";
        private const string WEBSERVICE_URL = "%%WEBSERVICE_URL%%";
        private const string DEFAULT_VALUE = "%%DEFAULT_VALUE%%";

        static public string Webservice_Url 
        {
            get { return WEBSERVICE_URL; } 
        }

        /// <summary>
        /// Loads string resources from text files embedded
        /// </summary>
        public static void ReadTemplateFiles()
        {
            try
            {
                // Read from resource file.
                using (Stream actStream = new MemoryStream(Encoding.UTF8.GetBytes(Resource.ActivitySkeleton)))
                {
                    using (StreamReader actSR = new StreamReader(actStream))
                    {
                        activitySkeleton = actSR.ReadToEnd();
                        actSR.Close();
                        actStream.Close();
                    }
                }

                using (Stream depPropStream = new MemoryStream(Encoding.UTF8.GetBytes(Resource.DependencyPropertySkeleton)))
                {
                    using (StreamReader depPropSR = new StreamReader(depPropStream))
                    {
                        depPropSkeleton = depPropSR.ReadToEnd();
                        depPropSR.Close();
                        depPropStream.Close();
                    }
                }

                using (Stream executeStream = new MemoryStream(Encoding.UTF8.GetBytes(Resource.ExecuteMethodBodySkeleton)))
                {
                    using (StreamReader executeSR = new StreamReader(executeStream))
                    {
                        executeSkeleton = executeSR.ReadToEnd();
                        executeSR.Close();
                        executeStream.Close();
                    }
                }
            }
            catch (Exception exp)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("ActivityTemplateReadingError" + exp.Message));
            }
        }

        #region Generate Activity Code

        /// <summary>
        /// Generates the activity code.
        /// </summary>
        /// <param name="wsModel">The ws model.</param>
        /// <returns></returns>
        public static string GenerateActivityCode(WebServiceWizardModel wsModel)
        {
            try
            {
                StringBuilder bodyBuff = new StringBuilder();
                StringBuilder propBuff = new StringBuilder();

                String actScratch = activitySkeleton;
                string bodyScratch = executeSkeleton;
                activityClassName = wsModel.ActivityName;

                Type modelType = wsModel.ActivityTypes[wsModel.SelectedWebMethod];

                // Generate the Dependency Properties
                StringBuilder paramValuesBuff = new StringBuilder();
                int paramIndex = 0;
                WebMethodParameterInfo outputParam = null;
                foreach (WebMethodParameterInfo param in wsModel.ParameterInfo[wsModel.SelectedWebMethod])
                {
                    propBuff.Append(GenerateDependencyProperty(param, activityClassName, param.DefaultValue));

                    if (string.IsNullOrEmpty(param.ActivityParamName))
                    {
                        param.ActivityParamName = param.WebMethodName;
                    }

                    if (param.ParamDirection == ParameterDirection.Out)
                    {
                        outputParam = param;
                    }
                    else
                    {
                        //No need to add built in params to request
                        if (param.IsBuiltIn)
                        {
                            continue;
                        }
                        if (paramIndex > 0)
                            paramValuesBuff.Append(", ");

                        paramIndex++;
                        paramValuesBuff.Append(param.ActivityParamValidName);
                    }
                }

                // Generate the Execute method body
                if (null != outputParam)
                {
                    bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.OUTPUT_TYPE, outputParam.ParamType);
                    bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.OUTPUT_PARAMETER_NAME, outputParam.ActivityParamValidName);
                    bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.VOID_OUTPUT_TYPE, "//");
                }
                else
                {
                    bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.OUTPUT_TYPE, "//");
                    bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.OUTPUT_PARAMETER_NAME, "//");
                    bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.VOID_OUTPUT_TYPE, "");
                }
                bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.PROXY_METHOD_NAME, wsModel.SelectedWebMethod);
                bodyScratch = bodyScratch.Replace(ActivityCodeGenerator.PARAMETER_VALUES, paramValuesBuff.ToString());
                bodyBuff.Append(bodyScratch);

                // Generate the Activity code
                actScratch = actScratch.Replace(ActivityCodeGenerator.METHOD_DESCRIPTION, wsModel.ActivityDescription);
                actScratch = actScratch.Replace(ActivityCodeGenerator.CATEGORY_NAME, wsModel.ActivityCategory);
                actScratch = actScratch.Replace(ActivityCodeGenerator.EXECUTION_BODY, bodyBuff.ToString());
                actScratch = actScratch.Replace(ActivityCodeGenerator.DEPENDENCY_PROPERTIES, propBuff.ToString());
                actScratch = actScratch.Replace(ActivityCodeGenerator.METHOD_NAME, wsModel.ActivityName);
                actScratch = actScratch.Replace(ActivityCodeGenerator.ACTIVITY_NAMESPACE, WebServiceWizardModel.ActivityNamespace);
                actScratch = actScratch.Replace(ActivityCodeGenerator.ACTIVITY_CLASS_NAME, activityClassName);
                actScratch = actScratch.Replace(ActivityCodeGenerator.PROXY_CLASS_NAME, modelType.Name);
                actScratch = actScratch.Replace(ActivityCodeGenerator.WEBSERVICE_URL, wsModel.WebServiceUrl.ToString());
                

                return actScratch;
            }
            catch (Exception exp)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("ActivityTemplateReadingError" + exp.Message));
                return null;
            }
        }

        /// <summary>
        /// Generates dependency properties for activity.
        /// </summary>
        /// <param name="param">parameter data.</param>
        /// <param name="actClassName">Activity class name.</param>
        /// <returns></returns>
        private static string GenerateDependencyProperty(WebMethodParameterInfo param, string actClassName, object defaultValue)
        {
            try
            {
                string scratch = depPropSkeleton;

                scratch = scratch.Replace(ActivityCodeGenerator.PARAMETER_NAME, param.ActivityParamName);
                scratch = scratch.Replace(ActivityCodeGenerator.PARAMETER_VALID_NAME, param.ActivityParamValidName);
                scratch = scratch.Replace(ActivityCodeGenerator.PARAMETER_TYPE, param.ParamType);
                scratch = scratch.Replace(ActivityCodeGenerator.ACTIVITY_CLASS_NAME, actClassName);

                if (param.ParamDirection == ParameterDirection.In)
                    scratch = scratch.Replace(ActivityCodeGenerator.INPUT_OUTPUT_ATTRIBUTE, ActivityCodeGenerator.REQUIRED_INPUT_PARAM_ATTRIBUTE);
                else if (param.ParamDirection == ParameterDirection.Out)
                    scratch = scratch.Replace(ActivityCodeGenerator.INPUT_OUTPUT_ATTRIBUTE, ActivityCodeGenerator.OUTPUT_PARAM_ATTRIBUTE);
                
                //replace the default value with null if nothing is specified
                string defValue = ((string)defaultValue ?? string.Empty );
                scratch = scratch.Replace(ActivityCodeGenerator.DEFAULT_VALUE, defValue );
                scratch = scratch.Replace(ActivityCodeGenerator.METHOD_DESCRIPTION, "Auto generated web service method");

                return scratch + "\n";
            }
            catch (Exception exp)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("ActivityTemplateReadingError" + exp.Message));
                return null;
            }
        }

        #endregion
    }
}
